﻿using System.Collections.Immutable;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService.Symbols;

/// <summary>
///     IBM DataCollection Script符号表基类，此符号表能够添加、删除、查找存储的符号。
///     符号使用符号名检索，符号名是否区分大小写依据具体用途决定。存储宏定义符号的符号表区分大小写，
///     其余情况不区分大小写。
/// </summary>
public abstract class SymbolTable : ReadOnlySymbolTable
{
    /// <summary>
    ///     保存文本节点和符号的对应关系。一般情况下，只有表达式节点才会添加到此集合中。
    /// </summary>
    private readonly Dictionary<ITextNode, ISymbol> _referenceStorage = new();

    private Dictionary<string, ModuleSymbol>? _importedModules;


    /// <summary>
    ///     保存本地符号的字典，由于宏符号名大小写敏感，其他符号不敏感，因此，此属性由派生类实现。
    /// </summary>
    protected abstract Dictionary<string, ISymbol> Symbols { get; }

    public override int Count => Symbols.Count;


    /// <summary>
    ///     清空符号表中的已有符号和导入符号表。
    /// </summary>
    public void Clear()
    {
        Symbols.Clear();
        _importedModules?.Clear();
        _importedModules = null;
    }


    /// <summary>
    ///     当添加的符号名已存在时触发的事件，回调函数的第一个参数是表中已有的符号，
    ///     第二个参数是重复声明的符号。
    /// </summary>
    public event Action<ISymbol, ISymbol>? SymbolNameConflict;


    private void OnSymbolNameConflict(ISymbol oldSymbol, ISymbol newSymbol)
    {
        SymbolNameConflict?.Invoke(oldSymbol, newSymbol);
    }


    /// <summary>
    ///     绑定语法节点对应的语法符号。
    /// </summary>
    /// <param name="node">语法节点</param>
    /// <param name="symbol">节点对应的的符号</param>
    public void BindReference(ITextNode node, ISymbol symbol)
    {
        _referenceStorage.TryAdd(node, symbol);
        symbol.Reference(node.GetLocation());
    }


    protected override ISymbol? LookupSymbolLocalCore(string symbolName)
    {
        return Symbols.GetValueOrDefault(symbolName);
    }


    protected override ISymbol? LookupSymbolDeclareCore(string[] qualifiedName)
    {
        return qualifiedName.Aggregate<string, ISymbol?>(null, (current, name) => current switch
        {
            null => Symbols.GetValueOrDefault(name),
            { Kind: SymbolKinds.Module } => ((ModuleSymbol)current).LookupNamedMember(name),
            { Kind: SymbolKinds.NamedType } => ((ITypeSymbol)current).LookupNamedMember(name),
            _ => current
        });
    }

    protected override ISymbol? LookupSymbolInImportedModuleCore(string[] qualifiedName)
    {
        return _importedModules?.Values
            .Select(module => module.LookupNamedMember(qualifiedName))
            .OfType<ISymbol>()
            .FirstOrDefault();
    }

    protected override void ImportModuleCore(ModuleSymbol module)
    {
        _importedModules ??= new Dictionary<string, ModuleSymbol>(StringComparer.OrdinalIgnoreCase);
        _importedModules.TryAdd(module.FullName, module);
    }

    protected override bool IsModuleImported(string moduleName)
    {
        return _importedModules?.ContainsKey(moduleName) == true;
    }

    public override bool ContainsName(string name)
    {
        return Symbols.ContainsKey(name);
    }

    public override ISymbol? GetSymbolOfExpression(Expression expression)
    {
        return _referenceStorage.GetValueOrDefault(expression);
    }


    public override IEnumerator<ISymbol> GetEnumerator()
    {
        return Symbols.Values.GetEnumerator();
    }


    /// <summary>
    ///     将指定符号表合并进当前符号表。
    /// </summary>
    /// <param name="table">指定加入的表格</param>
    public virtual void Join(ReadOnlySymbolTable table)
    {
        foreach (ISymbol sym in table)
        {
            if (Symbols.TryGetValue(sym.Name, out ISymbol? symbol))
            {
                OnSymbolNameConflict(sym, symbol);
            }
            else
            {
                Symbols.Add(sym.Name, sym);
            }
        }
    }


    protected virtual bool DeclareSymbolCore(ISymbol symbol)
    {
        if (Symbols.TryGetValue(symbol.Name, out ISymbol? old))
        {
            OnSymbolNameConflict(old, symbol);
            return old.Redeclare(symbol);
        }

        Symbols.Add(symbol.Name, symbol);
        return true;
    }


    /// <summary>
    ///     声明符号到符号表。
    /// </summary>
    /// <param name="symbol">需要声明的符号</param>
    public bool DeclareSymbol(ISymbol symbol)
    {
        return DeclareSymbolCore(symbol);
    }


    public override ImmutableArray<ISymbol> ToImmutable()
    {
        return [..Symbols.Values];
    }
}